#ifndef _io_h_
#define _io_h_

// io.h
//
// Image I/O

//Prevents Windows from complaining about long symbol names
#ifdef WIN32
#pragma warning(disable:4786)
#endif

#include "bstring.h"
#include "image/image.h"

#include <string>
#include <map>
#include <iostream>

namespace simple{

    // 
    // ImageIOBase
    //
    // Base class for all Image storage delegates.

    struct DelegateInfo
    {
      bool         isDelegate;    // true if delegate knows this format
      Rect         boundary;      // Image boundary (if known)
      unsigned int bytesPerPixel; // Bytes per pixel (if known)
      unsigned int planes;        // Number of planes (if known) 0=mono, 3=color
      std::string  format;        // Additional image format information
    };
    // Structure filled in by info() method


    struct DelegateParams
    {
      float       quality;       // Quality parameter (depends on format)
      std::string params;        // Other parameters

      DelegateParams (float qual=90.) : quality (qual) {}

    };


    class ImageIOBase
    {
    public:
        virtual DelegateInfo info (const std::string& filename) = 0;
        // See if this file can be read by this delegate.

        template<class T, class S>
        void read (const std::string& filename, Image<T,S>& image)
        {
            if (typeid(Image<RGB>) == typeid(image))
                image = readRGB (filename);
            else if (typeid(Image<Pel8>) == typeid(image))
                image = readPel8 (filename);
            else
                copy (readRGB (filename), image);
        }
        // This needs run-time type identification. 
        // You can do without it, but you will wind up with an additional
        // image copy when you read an RGB, or Pel8 image.


        template<class T, class S>
        void write (const std::string& filename, Image<T,S>& image, 
                    const DelegateParams& params = sNoParams)
        {
            if (typeid(Image<RGB>) == typeid(image))
                write (filename, image.storage(), params);
            else if (typeid(Image<Pel8>) == typeid(image))
                write (filename, image.storage(), params);
            else {
                Image<RGB> rgb = image;
                write (filename, rgb.storage(), params);
            }
        }
        // This needs run-time type identification. Performing these
        // checks allows us to coerce out image into a popular format for
        // storage. You can do this in a specific image delegate, but
        // it works nicely here.


        virtual Image<RGB> readRGB   (const std::string& filename) = 0;
        virtual Image<Pel8>  readPel8  (const std::string& filename) = 0;
        // Most file formats have limited ways that files are stored. These 
        // functions read RGB and 8-bit monochrome images.

        virtual bool write (const std::string& filename, 
                            const RectImageStorage& pixels, 
                            const DelegateParams& params) = 0;


        // Default copy constructor and assignment operators ok.
    protected:
        ImageIOBase ();
        virtual ~ImageIOBase ();

        static DelegateParams sNoParams;
    };




    //
    // ImageIODelegateList
    //
    class ImageIODelegateList
    {
    public:
        static ImageIODelegateList& gOnly ();
        // Our only instance

        ImageIOBase* getDelegate (const std::string& type);
        void setDelegate (const std::string& type, ImageIOBase* object);
        // Get/set delegate information

        ImageIOBase* findDelegate (const std::string& filename);
        // Return the delegate to handle this file, or null

    private:
        typedef std::map<std::string, ImageIOBase*> map;
        typedef map::iterator iterator;

        map _map;   // Our mapping of file type to a delegate to handle it

        static ImageIODelegateList* _sOnly;

        ImageIODelegateList ();
    };

}


#endif // _io_h_
